home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-06-11 | 14.8 KB | 467 lines | [TEXT/KAHL] |
- // Pop Up menu CDEF
- // (C) 1990-1995 Stuart Cheshire <cheshire@cs.stanford.edu>
- // Written after endless frustration with the one by Chris Faigle which
- // used to crash all the time, to whom some credit for inspiration should go.
-
- // Note: The MacTraps library is required, but only for GetHandleSize
- // Hopefully the linker should be smart enough not to include all of MacTraps
-
- #include <Traps.h>
-
- #define FIXED_MENU_WIDTH 1
- #define NEW_STYLE 2
- #define USE_ADD_RES_MENU 4
- #define RIGHT_JUSTIFY_TITLE 8
- #define OVERRIDE_MENU_SIZE (FIXED_MENU_WIDTH | NEW_STYLE)
-
- #define BOTTOM_SPACE 5
- #define LEFT_SPACE 13
- #define INACTIVE 255
- #define EXTRA_SPACE_FOR_NEW_STYLE 13
- #define NULL_MENU_WIDTH 60
-
- #define SICN_SPACE 20
- #define RICN_SPACE 20
- #define ICON_SPACE 36
-
- #define RICN_MENU 29
- #define SICN_MENU 30
-
- #define NORMAL_MENU_SIZE 16
- #define RICN_MENU_SIZE 20
- #define SICN_MENU_SIZE 20
- #define ICON_MENU_SIZE 36
-
- typedef short SICN[16];
- typedef SICN *SICNList;
- typedef SICNList *SICNHand;
-
- typedef struct
- {
- MenuHandle menu; // Handle to the actal menu in memory
- short menuID; // Id of the menu
- short resID; // resource id of menu to use, or zero if none
- short width, font, size; // width, font and size to draw the title in
- } pop_up_data;
-
- #define POPDATA(C) ((pop_up_data *)((C)[0]->contrlData[0]))
-
- #ifndef calcCntlRgn
- #define calcCntlRgn 10
- #endif
-
- #ifndef calcThumbRgn
- #define calcThumbRgn 11
- #endif
-
- #ifndef NULL
- #define NULL 0L
- #endif
-
- static MenuHandle get_pop_menu(ControlHandle cntrl)
- {
- if (POPDATA(cntrl)->resID) return(POPDATA(cntrl)->menu);
- else return((MenuHandle)cntrl[0]->contrlRfCon);
- }
-
- // If the control is untitled, then calc_rects returns title_rect as an empty rectangle
-
- static void calc_rects(ControlHandle cntrl, short ctype, Rect *title_rect, Rect *menu_rect)
- {
- Rect *control_rect = &cntrl[0]->contrlRect;
- MenuHandle the_menu = get_pop_menu(cntrl);
- short menu_height = NORMAL_MENU_SIZE;
-
- if (the_menu)
- {
- CalcMenuSize(the_menu);
- // If valid menu selection, work out the menu item height
- if (cntrl[0]->contrlValue >= 1 &&
- cntrl[0]->contrlValue <= CountMItems(the_menu))
- {
- short command_char;
- GetItemCmd(the_menu, cntrl[0]->contrlValue, &command_char);
- switch(command_char)
- {
- short icon;
- case SICN_MENU: menu_height = SICN_MENU_SIZE; break;
- case RICN_MENU: menu_height = RICN_MENU_SIZE; break;
- default: GetItemIcon(the_menu, cntrl[0]->contrlValue, &icon);
- if (icon) menu_height = ICON_MENU_SIZE;
- break;
- }
- }
- }
-
- // Work out top, left and bottom of menu rectangle and title rectangle
- menu_rect->left = control_rect->left;
- menu_rect->top = (control_rect->top + control_rect->bottom - menu_height)>>1;
- menu_rect->bottom = menu_rect->top + menu_height;
-
- title_rect->top = (control_rect->top+control_rect->bottom)/2-8;
- title_rect->left = control_rect->left;
- title_rect->bottom = title_rect->top+16;
- title_rect->right = control_rect->left;
-
- if (cntrl[0]->contrlTitle[0]) // If this control has a title
- {
- TextFont(POPDATA(cntrl)->font);
- TextSize(POPDATA(cntrl)->size);
- TextFace(0);
- if (POPDATA(cntrl)->width)
- title_rect->right += POPDATA(cntrl)->width;
- else title_rect->right += StringWidth(cntrl[0]->contrlTitle)+1;
- menu_rect->left = title_rect->right+3;
- }
-
- if (ctype & FIXED_MENU_WIDTH) menu_rect->right = control_rect->right;
- else
- {
- if (the_menu && the_menu[0] && the_menu[0]->menuWidth > 2)
- menu_rect->right=menu_rect->left+the_menu[0]->menuWidth;
- else menu_rect->right=menu_rect->left+NULL_MENU_WIDTH;
-
- if (ctype & NEW_STYLE) menu_rect->right += EXTRA_SPACE_FOR_NEW_STYLE;
- }
- }
-
- static void draw_menu_content(ControlHandle cntrl, MenuHandle the_menu, Rect menu_rect)
- {
- Str255 current_selection_text;
- short command_char, which_icon, text_x;
- Rect icon_rect;
- Handle icon_handle;
- FontInfo font_info;
- Style text_style;
-
- TextFont(systemFont);
- TextSize(0);
- TextFace(0);
- GetFontInfo(&font_info); // Get System font information to draw menu text
-
- // If control value is out of range for the menu,
- // display the menu's title instead
- if (cntrl[0]->contrlValue < 1 ||
- cntrl[0]->contrlValue > CountMItems(the_menu))
- {
- BlockMoveData(the_menu[0]->menuData, current_selection_text,
- 1+the_menu[0]->menuData[0]);
- text_x = menu_rect.left+LEFT_SPACE+2;
- }
- else
- {
- GetItem(the_menu,cntrl[0]->contrlValue,current_selection_text);
- GetItemCmd(the_menu,cntrl[0]->contrlValue,&command_char);
- GetItemStyle(the_menu, cntrl[0]->contrlValue, &text_style);
- TextFace(text_style);
- switch(command_char)
- {
- case SICN_MENU:
- {
- short sicn;
- BitMap src_bits = { 0, 2, {0,0,16,16}};
- SICNHand sicn_hand;
- icon_rect.top = menu_rect.top+3,
- icon_rect.left = menu_rect.left+LEFT_SPACE;
- icon_rect.bottom = menu_rect.top+3+16;
- icon_rect.right = menu_rect.left+LEFT_SPACE+2+16;
- GetItemIcon(the_menu, cntrl[0]->contrlValue, &sicn);
- if (sicn_hand = (SICNHand)GetResource('SICN', sicn+256))
- {
- HLock((Handle)sicn_hand);
- src_bits.baseAddr = (Ptr)*sicn_hand;
- if(GetHandleSize((Handle)sicn_hand) >= sizeof(SICN))
- CopyBits(&src_bits, &cntrl[0]->contrlOwner->portBits,
- &src_bits.bounds,&icon_rect,srcCopy,NULL);
- HUnlock((Handle)sicn_hand);
- }
- text_x = menu_rect.left+LEFT_SPACE+SICN_SPACE;
- break;
- }
-
- case RICN_MENU:
- GetItemIcon(the_menu,cntrl[0]->contrlValue,&which_icon);
- icon_handle=GetResource('ICON',which_icon+256);
- if(icon_handle)
- {
- icon_rect.left = menu_rect.left+LEFT_SPACE+2;
- icon_rect.right = icon_rect.left+16;
- icon_rect.top = (menu_rect.bottom+menu_rect.top-16)/2;
- icon_rect.bottom = icon_rect.top+16;
- PlotIcon(&icon_rect,icon_handle);
- }
- text_x = menu_rect.left+LEFT_SPACE+RICN_SPACE;
- break;
-
- default:
- GetItemIcon(the_menu,cntrl[0]->contrlValue,&which_icon);
- if (which_icon==0) text_x = menu_rect.left+LEFT_SPACE+2;
- else
- {
- icon_handle=GetResource('ICON',which_icon+256);
- if(icon_handle!=NULL)
- {
- icon_rect.left = menu_rect.left + LEFT_SPACE+2;
- icon_rect.right = icon_rect.left + 32;
- icon_rect.top = (menu_rect.bottom + menu_rect.top-32)/2;
- icon_rect.bottom = icon_rect.top+32;
- PlotIcon(&icon_rect,icon_handle);
- }
- text_x = menu_rect.left+LEFT_SPACE+ICON_SPACE;
- }
- break;
- }
- }
-
- MoveTo(text_x,(menu_rect.top+menu_rect.bottom+font_info.ascent-font_info.descent)/2);
- DrawString(current_selection_text);
- }
-
- static void draw(short ctype, ControlHandle cntrl)
- {
- Rect title_rect, menu_rect;
- FontInfo font_info;
- MenuHandle the_menu = get_pop_menu(cntrl);
-
- PenNormal();
- calc_rects(cntrl, ctype, &title_rect, &menu_rect);
- GetFontInfo(&font_info); // calc_rects sets the current font to the title font
-
- // Draw the title
- if (cntrl[0]->contrlTitle[0])
- {
- short v = (title_rect.top+title_rect.bottom+font_info.ascent-font_info.descent)/2;
- if (!(ctype & RIGHT_JUSTIFY_TITLE)) MoveTo(title_rect.left+1, v);
- else MoveTo(title_rect.right - StringWidth(cntrl[0]->contrlTitle), v);
- DrawString(cntrl[0]->contrlTitle);
- }
-
- // Draw the menu content region
- EraseRect(&menu_rect); // Erase content region of control
- InsetRect(&menu_rect,-1,-1); // Enlarge control by one pixel
- FrameRect(&menu_rect); // Frame just outside the erased region
-
- // If active control, draw drop shadow, else erase drop shadow
- if(cntrl[0]->contrlHilite == INACTIVE) PenMode(patBic);
- MoveTo(menu_rect.right,menu_rect.top+2);
- LineTo(menu_rect.right,menu_rect.bottom);
- LineTo(menu_rect.left+2,menu_rect.bottom);
- if(cntrl[0]->contrlHilite == INACTIVE) PenMode(patCopy);
-
- // If new style, draw the little arrow
- if (ctype & NEW_STYLE)
- {
- short i, h = menu_rect.right-10, v = (menu_rect.bottom+menu_rect.top)/2-3;
- for (i=0;i<6;++i) { MoveTo(h+i-6,v+i); Line(10-(i*2),0); }
- }
-
- if (the_menu) draw_menu_content(cntrl, the_menu, menu_rect);
-
- if(cntrl[0]->contrlHilite == INACTIVE)
- {
- long dimpat[2]; // WARNING! cannot use ANY globals
- dimpat[0] = 0xAA55AA55;
- dimpat[1] = 0xAA55AA55;
- PenPat((ConstPatternParam)dimpat);
- PenMode(patBic);
- if (cntrl[0]->contrlTitle[0]) PaintRect(&title_rect);
- InsetRect(&menu_rect,1,1);
- PaintRect(&menu_rect);
- }
- }
-
- // ************************************************************************
-
- // Yuk! PopUpMenuSelect calls CalcMenuSize, so we have to patch CalcMenuSize
- // to stop it resetting the menu size to the original value
-
- long _realwidth(void);
- typedef struct { unsigned short opcode; unsigned long menuSize; } movei;
-
- static pascal void CalcMenuSize_patch(MenuHandle m)
- {
- asm {
- move.l m, a0
- move.l (a0), a0
- extern _realwidth:
- move.l #0x12345678, MenuInfo.menuWidth(a0)
- }
- }
-
- static Boolean TrapAvailable(unsigned long trap)
- {
- TrapType tType = (trap & 0x800 ? ToolTrap : OSTrap);
- if (trap & 0x800) // if it is a ToolBox Trap
- {
- unsigned long n = 0x400; // number of toolbox traps
- if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap))
- n = 0x200;
- if ((trap &= 0x7FF) >= n) trap = _Unimplemented;
- }
- return(NGetTrapAddress(trap, tType) != NGetTrapAddress(_Unimplemented, ToolTrap));
- }
-
- // drag returns 1 if a menu item is selected (even if it is the same as
- // the already selected item)
- static long drag(short ctype, ControlHandle cntrl)
- {
- long retval = 0;
- Point point;
- short old_choice;
- long chosen;
- MenuHandle the_menu = get_pop_menu(cntrl);
- Rect title_rect, menu_rect;
- short original_menuwidth;
- unsigned long *menuSize = &((movei*)_realwidth)->menuSize;
- void *oldtrap;
-
- if (!the_menu) return;
- calc_rects(cntrl, ctype, &title_rect, &menu_rect);
- point.v=menu_rect.top;
- point.h=menu_rect.left;
- LocalToGlobal(&point);
- if (cntrl[0]->contrlTitle[0]) InvertRect(&title_rect);
- old_choice = cntrl[0]->contrlValue;
- if (old_choice < 1 || old_choice > CountMItems(the_menu))
- old_choice = 0;
- InsertMenu(the_menu,-1);
- CalcMenuSize(the_menu);
- if (ctype & FIXED_MENU_WIDTH) the_menu[0]->menuWidth = menu_rect.right - menu_rect.left;
- else if (ctype & NEW_STYLE) the_menu[0]->menuWidth += EXTRA_SPACE_FOR_NEW_STYLE;
- if (ctype & OVERRIDE_MENU_SIZE)
- {
- oldtrap = GetToolTrapAddress(_CalcMenuSize);
- original_menuwidth = the_menu[0]->menuWidth;
- SetToolTrapAddress((ProcPtr)CalcMenuSize_patch, _CalcMenuSize);
- *menuSize = *(unsigned long *)&the_menu[0]->menuWidth;
- if (TrapAvailable(_HWPriv)) FlushInstructionCache();
- }
- chosen = PopUpMenuSelect(the_menu, point.v, point.h, old_choice);
- if (ctype & OVERRIDE_MENU_SIZE)
- {
- the_menu[0]->menuWidth = original_menuwidth;
- SetToolTrapAddress(oldtrap, _CalcMenuSize);
- }
- if (cntrl[0]->contrlTitle[0]) InvertRect(&title_rect);
- DeleteMenu(the_menu[0]->menuID);
- if (chosen & 0xFFFF0000) // If some item was chosen
- {
- retval = 1;
- if ((chosen & 0xFFFF) != old_choice) // If choice has changed
- {
- cntrl[0]->contrlValue = (chosen & 0xFFFF);
- InsetRect(&menu_rect,-1,-1);
- menu_rect.bottom += 1;
- menu_rect.right += 1;
- EraseRect(&menu_rect);
- if (cntrl[0]->contrlVis) draw(ctype,cntrl);
- }
- }
- return(retval);
- }
-
- // Note: subtle problems here.
- // The menu resource read with GetMenu is detached because ResEdit has a tendency to
- // create multiple concurrent invocations of the same control (eg in the DLOG window
- // and in the DITL window) at the same time. If all the CNTLs share the same menu
- // resource in memory, then as soon as one of them is disposed, the resource gets
- // released and all the others crash when they try to access it. (There is no
- // reference counting on number of clients accessing a resource.) Consequently we
- // give each instance its own private copy of the menu by calling DetachResource.
-
- pascal long main(short ctype, ControlHandle cntrl, short message, long parameter)
- {
- Rect title_rect, menu_rect;
- long retval = 0;
- PenState save_pen;
- short save_size;
- short save_font;
- Style save_face;
-
- HLock((Handle)cntrl);
- if (message != initCntl)
- {
- if (!cntrl[0]->contrlData)
- {
- DebugStr("\pError: Popup Not Initialized");
- HUnlock((Handle)cntrl);
- return(-1);
- }
- HLock(cntrl[0]->contrlData);
- }
- GetPenState(&save_pen);
- save_font=cntrl[0]->contrlOwner->txFont;
- save_size=cntrl[0]->contrlOwner->txSize;
- save_face=cntrl[0]->contrlOwner->txFace;
-
- ctype &= 15;
- switch(message)
- {
- case drawCntl : if (cntrl[0]->contrlVis) draw(ctype, cntrl);
- break;
- case testCntl : calc_rects(cntrl, ctype, &title_rect, &menu_rect);
- if (cntrl[0]->contrlHilite != INACTIVE)
- if (PtInRect(*(Point*)¶meter, &menu_rect))
- retval=1;
- // Note: used to return 131, which results in Control
- // Manager calling dragCntl instead of autoTrack, and
- // that results in trip DialogSelect not returning TRUE
- // and the item hit which results in the application
- // not knowing that the menu has been clicked on...
- break;
- case calcCRgns: parameter &= 0xFFFFFF; // Old style 24-bit call
- case calcCntlRgn: case calcThumbRgn : // New style 32-bit clean version
- calc_rects(cntrl, ctype, &title_rect, &menu_rect);
- menu_rect.top -= 1;
- menu_rect.left -= 1;
- menu_rect.bottom += 2;
- menu_rect.right += 2;
- UnionRect(&title_rect, &menu_rect, &menu_rect);
- RectRgn((RgnHandle)parameter, &menu_rect); break;
- break;
- case initCntl : cntrl[0]->contrlData = NewHandle(sizeof(pop_up_data));
- if (!cntrl[0]->contrlData) DebugStr("\pNo memory");
- cntrl[0]->contrlAction = (ControlActionUPP)-1;
- // The info is copied out of the contrlMin/Max fields,
- // which are then reset so that SetCtlValue calls will
- // work.
- POPDATA(cntrl)->menu = NULL;
- POPDATA(cntrl)->menuID = 0;
- POPDATA(cntrl)->width = cntrl[0]->contrlMax;
- POPDATA(cntrl)->resID = cntrl[0]->contrlMin;
- POPDATA(cntrl)->font = 0; // Font and size no
- POPDATA(cntrl)->size = 0; // longer supported
- cntrl[0]->contrlMin = 0;
- cntrl[0]->contrlMax = 0x7FFF;
- if (POPDATA(cntrl)->resID)
- {
- POPDATA(cntrl)->menu = GetMenu(POPDATA(cntrl)->resID);
- POPDATA(cntrl)->menuID = POPDATA(cntrl)->menu[0]->menuID;
- DetachResource((Handle)POPDATA(cntrl)->menu);
- if (ctype & USE_ADD_RES_MENU)
- {
- AddResMenu(POPDATA(cntrl)->menu, cntrl[0]->contrlRfCon);
- //CalcMenuSize(POPDATA(cntrl)->menu);
- }
- cntrl[0]->contrlMax = CountMItems(POPDATA(cntrl)->menu);
- }
- break;
- case dispCntl : if (POPDATA(cntrl)->resID)
- DisposHandle((Handle)POPDATA(cntrl)->menu);
- DisposHandle(cntrl[0]->contrlData);
- cntrl[0]->contrlData = NULL;
- break;
- case posCntl : break;
- case thumbCntl: break;
- case dragCntl : break;
- case autoTrack: retval=drag(ctype, cntrl); break;
- }
- SetPenState(&save_pen);
- TextFont(save_font);
- TextSize(save_size);
- TextFace(save_face);
- if (message != dispCntl) HUnlock(cntrl[0]->contrlData);
- HUnlock((Handle)cntrl);
- return(retval);
- }
-